import type {
  UseModalReturnType,
  ModalMethods,
  ModalProps,
  ReturnMethods,
  UseModalInnerReturnType,
} from '../typing';
import {
  ref,
  onUnmounted,
  unref,
  getCurrentInstance,
  reactive,
  watchEffect,
  nextTick,
  toRaw,
  computed,
} from 'vue';
import { VxeModalInstance } from 'vxe-table';
import { isProdMode } from '@/utils/env';
import { isFunction } from '@/utils/is';
import { isEqual } from 'lodash-es';
import { tryOnUnmounted } from '@vueuse/core';
import { error } from '@/utils/log';

const dataTransfer = reactive<any>({});

const openData = reactive<{ [key: number]: boolean }>({});

/** 适用于弹窗的外部调用 */
export function useModal(): UseModalReturnType {
  const modal = ref<Nullable<ModalMethods>>(null);
  const loaded = ref<Nullable<boolean>>(false);
  const uid = ref<number>(0);

  function register(modalMethod: ModalMethods, uuid: number) {
    if (!getCurrentInstance()) {
      throw new Error('useModal() 只能在 setup() 或功能组件内部使用！');
    }
    uid.value = uuid;
    isProdMode() &&
      onUnmounted(() => {
        modal.value = null;
        loaded.value = false;
        dataTransfer[String(unref(uid))] = null;
      });
    if (unref(loaded) && isProdMode() && modalMethod === unref(modal)) return;

    modal.value = modalMethod;
    loaded.value = true;
  }

  const getInstance = () => {
    const instance = unref(modal);
    if (!instance) {
      error('useModal 实例未定义！');
    }
    return instance;
  };

  const methods: ReturnMethods = {
    getModal: (): VxeModalInstance | undefined => {
      return getInstance()?.getModal();
    },
    isOpen: (): boolean => {
      return getInstance()?.isOpen() || false;
    },
    setModalProps: (props: Partial<ModalProps>): void => {
      getInstance()?.setModalProps(props);
    },

    getOpen: computed((): boolean => {
      return openData[~~unref(uid)];
    }),
    openModal: <T = any>(open = true, data?: T, openOnSet = true): void => {
      getInstance()?.setModalProps({
        open,
      });

      if (!data) return;
      const id = unref(uid);
      if (openOnSet) {
        dataTransfer[id] = null;
        dataTransfer[id] = toRaw(data);
        return;
      }
      const equal = isEqual(toRaw(dataTransfer[id]), toRaw(data));
      if (!equal) {
        dataTransfer[id] = toRaw(data);
      }
    },
    closeModal: () => {
      getInstance()?.setModalProps({ open: false });
    },
  };
  return [register, methods];
}

/** 适用于弹窗的内部调用 */
export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => {
  const modalInstanceRef = ref<Nullable<ModalMethods>>(null);
  const currentInstance = getCurrentInstance();
  const uidRef = ref<number>(0);

  const getInstance = () => {
    const instance = unref(modalInstanceRef);
    if (!instance) {
      error('useModalInner 实例未定义！');
    }
    return instance;
  };

  const register = (modalInstance: ModalMethods, uuid: number) => {
    isProdMode() &&
      tryOnUnmounted(() => {
        modalInstanceRef.value = null;
      });
    uidRef.value = uuid;
    modalInstanceRef.value = modalInstance;
    currentInstance?.emit('register', modalInstance, uuid);
  };

  watchEffect(() => {
    const data = dataTransfer[unref(uidRef)];
    if (!data) return;
    if (!callbackFn || !isFunction(callbackFn)) return;
    nextTick(() => {
      callbackFn(data);
    });
  });

  return [
    register,
    {
      getModal: (): VxeModalInstance | undefined => {
        return getInstance()?.getModal();
      },
      isOpen: (): boolean => {
        return getInstance()?.isOpen() || false;
      },
      setModalProps: (props: Partial<ModalProps>): void => {
        getInstance()?.setModalProps(props);
      },

      getOpen: computed((): boolean => {
        return openData[~~unref(uidRef)];
      }),
      closeModal: () => {
        getInstance()?.setModalProps({ open: false });
      },
      changeLoading: (loading = true) => {
        getInstance()?.setModalProps({ loading });
      },
      changeCancelLoading: (loading = true) => {
        getInstance()?.setModalProps({ cancelLoading: loading });
      },
      changeOkLoading: (loading = true) => {
        getInstance()?.setModalProps({ okLoading: loading });
      },
    },
  ];
};
